package org.lex.game.sudoku;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Arrays;

import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.jdesktop.application.Application;
import org.lex.game.sudoku.util.Restorable;
import org.lex.game.sudoku.view.ApplicationView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SudokuApplication extends Application implements Restorable {
	public static void main(String[] args) {
		Application.launch(SudokuApplication.class, args);
	}

	private static final String SESSION_FILE_NAME = "session.conf";

	private JFrame mainFrame = null;
	private ApplicationView applicationView = null;
	private Prefs prefs = null;

	private Logger log = LoggerFactory.getLogger(SudokuApplication.class);

	// ///////////
	// Application
	// ///////////
	@Override
	protected void initialize(String[] args) {
		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			@Override
			public void uncaughtException(Thread t, Throwable e) {
				System.err.println("uncaught excepion in thread: " + t);
				e.printStackTrace(System.err);
			}
		});
		log.info("Application initialize with args=" + Arrays.toString(args));
		super.initialize(args);
		// plain font
		UIManager.put("swing.boldMetal", Boolean.FALSE);
		// enable anti-aliased text:
		System.setProperty("awt.useSystemAAFontSettings", "on");
		System.setProperty("swing.aatext", "true");
		// setup look and feel
		String lnf = this.getPrefs().getConfig().getProperty(Prefs.KEY_LNF);
		log.info("start up look&feel: " + lnf);

		if (null != lnf && false == lnf.trim().isEmpty()) {
			try {
				UIManager.setLookAndFeel(lnf);
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
					| UnsupportedLookAndFeelException e) {
				log.info("setup look&feel failed, fallback to cross platform look&feel", e);
				try {
					lnf = UIManager.getCrossPlatformLookAndFeelClassName();
					UIManager.setLookAndFeel(lnf);
				} catch (Exception ex) {
					// should not fail
					ex.printStackTrace();
				}
			}
		}
	}

	@Override
	protected void startup() {
		log.info("Application startup()");
		try {
			this.getContext().getSessionStorage().restore(getMainFrame(), SESSION_FILE_NAME);
		} catch (Exception e) {
			log.warn("restore session failed", e);
		}
		// show main view
		this.getMainFrame().setVisible(true);
	}

	@Override
	protected void ready() {
		log.info("Application ready()");
		this.restore();
	}

	@Override
	protected void shutdown() {
		log.info("Application shutdown()");
		this.store();
	}

	@Override
	protected void end() {
		log.info("Application end()");
		System.exit(0);
	}

	// ///////////
	// Restorable
	// ///////////
	@Override
	public void store() {
		this.getApplicationView().store();
		this.getPrefs().storeConfig();
		// store session
		try {
			this.getContext().getSessionStorage().save(getMainFrame(), SESSION_FILE_NAME);
		} catch (Exception e) {
			log.warn("store session failed", e);
		}
	}

	@Override
	public void restore() {
		this.getApplicationView().restore();
	}

	public long getStartTime() {
		return this.getApplicationView().getSudokuBoard().getStartTime();
	}

	public JFrame getMainFrame() {
		if (null == this.mainFrame) {
			this.mainFrame = new JFrame();
			this.mainFrame.setTitle("Sudoku");
			this.mainFrame.setName("mainFrame");
			this.mainFrame.setContentPane(this.getApplicationView());
			this.mainFrame.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					exit(e);
				}
			});
			this.mainFrame.pack();
		}
		return this.mainFrame;
	}

	public ApplicationView getApplicationView() {
		if (null == this.applicationView) {
			this.applicationView = new ApplicationView(this);
		}
		return applicationView;
	}

	public Prefs getPrefs() {
		if (null == prefs) {
			this.prefs = new Prefs(this.getContext().getLocalStorage().getDirectory());
		}
		return prefs;
	}
}
